#ifndef SERVER_LISTENER_H
#define SERVER_LISTENER_H



#include "../AI_Tester/includes.h"



//! @brief Player types
enum PlayerType { local, ai, network };



/** @brief Proxy for each client to identify the messages sender
*
*   \image html server_listener.jpeg
*/
class client_proxy: public QObject
{

	Q_OBJECT

public:

	/** @brief Constructor assignes client id
	*	@param c client id
	*	@param parent Qt parent
	*/
	client_proxy( unsigned int c, QObject* parent = 0 ): QObject( parent )
	{
		client_id = c;
	};


private:

	//! client id
	unsigned int client_id;


public slots:

	//! @brief Qt slot that receives message and passes it to the server with client's (sender's) id
	void receive_message();

	/** @brief Qt slot informing about changed network connection status
	*	@param state new status
	*/
	void status_changed( const QAbstractSocket::SocketState& state );

	/** @brief Qt slot informing about network connection error
	*	@param error network error
	*/
	void connection_error( const QAbstractSocket::SocketError& error );

};



/** @brief Class for communication with clients
*
*   \image html server_listener.jpeg
*/
class Server:public QTcpServer
{

	Q_OBJECT

public:

	/** @brief Constructor inits all arrays to given number of players
	*	@param plrs number of players in the network game
	*	@param parent Qt parent
	*/
	Server( unsigned int plrs, QObject* parent = 0 );
	//! @brief Disconnect all hosts, sends error message and destory all arrays
	~Server();

	/** @brief Sends message to specified socket
	*	@param s message to send
	*	@param r socket where to send message (client)
	*/
	void send_string( const string& s, QTcpSocket* r );

	/** @brief	Reads string from socket whnen message arrives
	*	@return	received message
	*/
	string read_string( QTcpSocket* r );

	//! Number of clients currently connected
	unsigned int clients_num;
	//! Maximum number of clients ( =number of players set )
	unsigned int max_clients;

	//! received message size
	quint16 block_size;

	//! array of clients connected
	QTcpSocket** clients;
	//! array of proxies for clients
	client_proxy** cproxy;
	//! array of messages to be send to all clients
	string* pending_messages;
	//! number of received pending messages
	unsigned int pending_messages_rcvd;
	//! if all players received all messages = map or objects received ( max_clients ) and all players received all players ( max_clients * max_clients )
	unsigned int ready_to_go;

	/** @brief reads and parses incomming message and runs appropriate actions (message passed from proxy)
	*	@param c sender's id
	*/
	void receive_message( unsigned int c );

	//! @brief Sends all pending messages to all clients
	void send_pending_messages();

	/** @brief Sends message to all clients except sender
	*	@param m message to be send
	*	@param s sender ( -1 -> send to all clients )
	*/
	void resend_message( const string& m, int s = -1 );

	//! @brief Restarts server, so new clients can connect
	void restart();

	/** @brief Sets listening port
	*	@param port listening port
	*/
	void set_port( quint16 port )
	{
		server_port = port;
	};


private:

	//! listening port
	quint16 server_port;


private slots:

	//! @brief Qt slot invoked when new client connected - connects client to proxy and sends him map, objects and accept message with id
	void add_client();
	//! @brief Qt slot invoked when some client disconnected
	void client_disconnected();
	//! @brief Qt slot invoked with delay when some client disconnected
	void restart_listener();

};



/** @brief Singleton to hnadle the network connections
*
*   \image html server_listener.jpeg
*/
class server_listener
{

public:
	
	/**	@brief	Function for obaining the singleton
	*	@return	pointer to the singleton
	*/
	static server_listener* get_instance();

	//! @brief Clears map and objects
	void rebuild()
	{
		if ( server != 0 )
		{
			server->close();
			for ( unsigned int i = 0; i < server->clients_num; ++i )
			{
				server->send_string( "ERROR=Server stopped.", server->clients[ i ] );
				server->clients[ i ]->disconnect();
				server->pending_messages[ i ] = "";
			}
			delete server;
			cout << "LISTENER STOPPED" << endl;
		}
		server = 0;
		map = "";
		objects = "";
	};

	//! pointer to the network client ( QTcpServer )
	Server* server;

	/** @brief	Loads map terrain from file
	*	@param	file file, where the map is stored
	*	first line contains size_x size_y, other lines terrain information
	*	10 - 100: terrain
	*	20000: water
	*	30000: wall
	*	number % 10: beginning place of player with this number
	*	@return	true, if loading is successful, else false
	*/
	bool load_map_file( const char* file );

	/** @brief	Loads map objects from file
	*	@param	file file, where the objects are stored
	*	file structure: "Type	Name	Pos_x	Pos_y	Health	Player_id"
	*	#: comment
	*	@return	true, if loading is successful, else false
	*/
	bool load_objects_file( const char* file );

	/** @brief Creates server for given number of players
	*	@param plrs number of players in game
	*/
	void create_listener( unsigned int plrs );
	/** @brief	Starts server to listen on given port
	*	@param	port port to listen on
	*	@return true if listener started successfully, else false
	*/
	bool start_listener( unsigned int port );
	//! @brief Restarts server, so new clients can connect
	void restart_listener();

	//! loaded map (MAP=map)
	string map;
	//! x size of loaded map
	unsigned int map_sx;
	//! y size of loaded map
	unsigned int map_sy;
	//! loaded objects (OBJECTS=objects)
	string objects;


	int x;


private:

	server_listener( int _x ): x( _x )
	{
		server = 0;
		map = "";
		objects = "";
	};
	server_listener( const server_listener& );
	server_listener& operator=( const server_listener& x );
	~server_listener()
	{
		if ( server != 0 )
			delete server;
	};

	/** @brief	Parses lines when loading objects from file
	*	@param	os where to put parsed line
	*	@param	ol line to parse
	*	@return	true if parsing successful, else false
	*/
	bool parse_objects_line( ostringstream& os, string ol );

};



#endif